package com.lensyn.generator.plugins;

import com.lensyn.generator.handler.ControllerNameHandler;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.internal.util.JavaBeansUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * Controller 生成器
 * @author xiaob@lensysn.com
 * @date 2016/12/20
 *
 * @see CUIDPlugin#generateController(IntrospectedTable, TopLevelClass, Interface)
 */
public class GenerateController {
    protected IntrospectedTable introspectedTable;

    protected TopLevelClass dtoClass;

    protected ControllerNameHandler controllerNameHandler;

    protected Interface anInterface;

    public GenerateController(IntrospectedTable introspectedTable, TopLevelClass dtoClass, ControllerNameHandler controllerNameHandler, Interface anInterface) {
        this.introspectedTable = introspectedTable;
        this.dtoClass = dtoClass;
        this.controllerNameHandler = controllerNameHandler;
        this.anInterface = anInterface;
    }


    public TopLevelClass generate() {
        TopLevelClass controller = new TopLevelClass(controllerNameHandler.controllerName(
                introspectedTable.getFullyQualifiedTable().getIntrospectedTableName()));
        controller.setVisibility(JavaVisibility.PUBLIC);

        controller.addImportedType("org.slf4j.Logger");
        controller.addImportedType("org.slf4j.LoggerFactory");
        controller.addImportedType(anInterface.getType());
        controller.addImportedType(dtoClass.getType());
        controller.addImportedType("org.springframework.beans.factory.annotation.Autowired");
        controller.addImportedType("org.springframework.web.bind.annotation.RequestMapping");
        controller.addImportedType("org.springframework.web.bind.annotation.RestController");
        controller.addImportedType("org.springframework.validation.BindingResult");
        controller.addImportedType("org.springframework.web.bind.annotation.*");


        controller.addImportedType("javax.validation.Valid");
        controller.addImportedType("java.util.List");


        FullyQualifiedJavaType returnType = getResponse();
        controller.addImportedType(returnType);

        controller.addAnnotation("@RestController");

        List<Field> fields = addFields(controller);
        for (Field field : fields) {
            controller.addField(field);
        }

        List<Method> methods = addMethods();
        for (Method method : methods) {
            controller.addMethod(method);
        }

        NameUtils.addClassComment(controller, introspectedTable, " Controller");

        return controller;
    }


    private List<Method> addMethods() {
        List<Method> methods = new ArrayList<Method>();
        methods.add(addMethod());
        methods.add(deleteMethod());
        methods.add(updateMethod());
        methods.add(findByIdMethod());


        return methods;
    }

    private List<Field> addFields(TopLevelClass controller) {
        List<Field> fields = new ArrayList<Field>();

        //logger
        Field field = NameUtils.logger(controller.getType().getShortName());
        fields.add(field);

        //service
        field = NameUtils.autowiredField(anInterface);
        fields.add(field);

        return fields;
    }


    private Method addMethod() {
        String baseURI = baseURI();
        FullyQualifiedJavaType responseType = getResponse();

        Method method = new Method();

        method.addAnnotation("@RequestMapping(value = \"" + baseURI + "\", method = RequestMethod.POST)");

        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(responseType);
        method.setName("add" + getEntityName(true));

        String dtoParamName = JavaBeansUtil.getValidPropertyName(dtoClass.getType().getShortName());
        Parameter parameter = new Parameter(dtoClass.getType(), dtoParamName);
        parameter.addAnnotation("@Valid");
        parameter.addAnnotation("@RequestBody");
        method.addParameter(parameter);
        Parameter parameter2 = new Parameter(new FullyQualifiedJavaType("org.springframework.validation.BindingResult")
                , "result");
        method.addParameter(parameter2);


        method.addBodyLine("if (result.hasErrors()) {");
        method.addBodyLine("return new Response().failure();");
        method.addBodyLine("}");
        method.addBodyLine(dtoParamName + " = " + getServiceName() + ".add" + getEntityName(true) + "(" + dtoParamName + ");");


        method.addBodyLine("return new Response().success(" + dtoParamName + ");");

        NameUtils.addMethodComment(method, "");
        return method;
    }

    private Method deleteMethod() {
        String baseURI = baseURI();
        baseURI += "/{ids}";

        FullyQualifiedJavaType responseType = getResponse();

        Method method = new Method();

        method.addAnnotation("@RequestMapping(value = \"" + baseURI + "\", method = RequestMethod.DELETE)");

        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(responseType);

        method.setName("delete" + getEntityName(true));


        Parameter parameter = new Parameter(new FullyQualifiedJavaType("java.lang.String"), "ids");
        parameter.addAnnotation("@PathVariable");
        method.addParameter(parameter);

        method.addBodyLine("Integer number = " + getServiceName() + ".delete" + getEntityName(true) + "(ids);");


        method.addBodyLine("return new Response().success(number);");

        NameUtils.addMethodComment(method, "");
        return method;
    }

    private Method updateMethod() {
        String baseURI = baseURI();
        baseURI += "/{id}";

        FullyQualifiedJavaType responseType = getResponse();

        Method method = new Method();

        method.addAnnotation("@RequestMapping(value = \"" + baseURI + "\", method = RequestMethod.PATCH)");

        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(responseType);
        method.setName("update" + getEntityName(true));

        Parameter parameter0 = new Parameter(new FullyQualifiedJavaType("java.lang.Long"), "id");
        parameter0.addAnnotation("@PathVariable");
        method.addParameter(parameter0);


        String dtoParamName = JavaBeansUtil.getValidPropertyName(dtoClass.getType().getShortName());
        Parameter parameter = new Parameter(dtoClass.getType(), dtoParamName);
        parameter.addAnnotation("@Valid");
        parameter.addAnnotation("@RequestBody");
        method.addParameter(parameter);

        Parameter parameter2 = new Parameter(new FullyQualifiedJavaType("org.springframework.validation.BindingResult")
                , "result");
        method.addParameter(parameter2);


        method.addBodyLine("if (result.hasErrors()) {");
        method.addBodyLine("return new Response().failure();");
        method.addBodyLine("}");
        method.addBodyLine(dtoParamName + " = " + getServiceName() + ".update" + getEntityName(true) + "(" + dtoParamName + ");");


        method.addBodyLine("return new Response().success(" + dtoParamName + ");");

        NameUtils.addMethodComment(method, "");
        return method;
    }

    private Method findByIdMethod() {
        String baseURI = baseURI();
        baseURI += "/{ids}";

        FullyQualifiedJavaType responseType = getResponse();

        Method method = new Method();

        method.addAnnotation("@RequestMapping(value = \"" + baseURI + "\", method = RequestMethod.GET)");

        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(responseType);
        method.setName("find" + getEntityName(true));


        Parameter parameter = new Parameter(new FullyQualifiedJavaType("java.lang.String"), "ids");
        parameter.addAnnotation("@PathVariable");
        method.addParameter(parameter);
        method.addBodyLine("List<"+dtoClass.getType().getShortName()+">" + " dtos = " + getServiceName() + ".find" + getEntityName(true) + "(ids);");

        method.addBodyLine("return new Response().success(dtos);");

        NameUtils.addMethodComment(method, "");
        return method;
    }

    private String baseURI() {
        String tableName = introspectedTable.getFullyQualifiedTable().getIntrospectedTableName();

        //资源复数
        tableName = NameUtils.plural(tableName);

        String baseURI = tableName.toLowerCase().replaceAll("_", "/");
        baseURI = "/" + baseURI;

        return baseURI;
    }






    private String getServiceName() {
        return JavaBeansUtil.getValidPropertyName(anInterface.getType().getShortName());
    }

    private FullyQualifiedJavaType getResponse() {
        return new FullyQualifiedJavaType("com.lensyn.common.utils.system.response.Response");
    }

    private String getEntityName(boolean firstUpper) {
        return JavaBeansUtil.getCamelCaseString(introspectedTable.getFullyQualifiedTable().getIntrospectedTableName(), firstUpper);
    }
}
